package br.com.senacrs.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

public class Dao<T> {
    
    private Transaction transaction;
    private Session session;
    private final Class<T> classe;

    /**
     * Recebe a sessão vigente e a classe a ser utilizada.
     * 
     * @param session
     * @param classe
     */
    public Dao(Session session, Class<T> classe) {
        this.session = session;
        this.classe = classe;
    }

    public void insert(T objeto) {
        this.beginTransaction();
        this.session.save(objeto);
        this.commit();
    }

    public void delete(T objeto) {
        this.beginTransaction();
        this.session.delete(objeto);
        this.commit();
    }

    public void update(T objeto) {
        this.beginTransaction();
        this.session.merge(objeto);
        this.commit();
    }

    @SuppressWarnings("unchecked")
    public List<T> selectAll() {
        return this.session.createCriteria(this.classe).list();
    }

    @SuppressWarnings("unchecked")
    public List<T> selectAllOrderBy(String order) {
        Criteria sql = getSession().createCriteria(this.classe, "x");
        order = "x.".concat(order);
        sql.addOrder(Order.asc(order));
        return sql.list();
    }

    @SuppressWarnings("unchecked")
    public T selectOne(Long id) {
        return (T) this.session.load(this.classe, id);
    }

    @SuppressWarnings("unchecked")
    public T selectOneGet(Long id) {
        return (T) this.session.get(this.classe, id);
    }

    /**
     * Recebe a String representando o CritÈrio de busca e a String de
     * comparaÁ„o, que será realizado por LIKE.<br/> 
     * Ex: Usando uma busca por Livro, onde a descricao contenha "Tempo"<br/>
     * criterio = "descricao", string = "Tempo"
     * 
     * @param criterio
     * @param string
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<T> selectWhere(String criterio, String string) {
        Criteria sql = getSession().createCriteria(this.classe, "x");
        criterio = "x.".concat(criterio);
        sql.add(Expression.like(criterio, string, MatchMode.ANYWHERE).ignoreCase());
        return sql.list();
    }

    /**
     * Recebe a String representando o CritÈrio de busca, que ser· realizado
     * comparando o ID da chave estrangeira.<br/> 
     * Ex: Usando uma busca por Livro, onde o id do gÍnero seja igual a 1<br/>
     * criterio = "genero.id", valor = 1L
     * 
     * @param criterio
     * @param valor
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<T> selectWhere(String criterio, Long valor) {
        Criteria sql = getSession().createCriteria(this.classe, "x");
        criterio = "x.".concat(criterio);
        sql.add(Expression.eq(criterio, valor));
        return sql.list();
    }

    /**
     * Utilizado para buscar os Ìtens em que o par‚metro de comparaÁ„o
     * est· contido em uma tabela referenciada (FK).<br/>
     * Ex: Usando uma busca por DVD, onde o id do Tipo seja igual a 3 (DVD - Shows)<br/>
     * criterio = "genero", criterioJoin = "tipo", valor = 1L
     * 
     * @param criterio
     * @param criterioJoin
     * @param valor
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<T> selectWhere(String criterio, String criterioJoin, Long valor){
        Criteria sql = getSession().createCriteria(this.classe, "x");
        criterio = "x.".concat(criterio);
        criterioJoin = "y.".concat(criterioJoin);
        sql.createAlias(criterio, "y")
                .createAlias(criterioJoin, "z")
                .add(Restrictions.eqProperty("y.id", criterio + ".id"))
                .add(Restrictions.eqProperty("z.id", criterioJoin + ".id"))
                .add(Restrictions.eq("z.id", valor));
        return sql.list();
    }

    public Session getSession() {
        return session;
    }

    public Class<T> getClasse() {
        return classe;
    }
    
    public void beginTransaction() {
        this.transaction = this.session.beginTransaction();
    }

    public void commit() {
        this.transaction.commit();
        this.transaction = null;
    }
    
}
